/*
    @file: log/source/LogBuilder.cpp
    @author: Wolfhead
 */
#include "LogBuilder.h"
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include "Log.h"
#include "detail/LogPriorityHelper.h"
#include "logger_creator/FileLoggerCreator.h"
#include "logger_creator/OStreamLoggerCreator.h"
#include "formatter_creator/DatetimeFormatterCreator.h"
#include "formatter_creator/StringFormatterCreator.h"

namespace logger
{

LogBuilder::LogBuilder()
{
    initialize_creator();
}

void LogBuilder::initialize_creator()
{
    register_logger_creator("FileLogger", 
                            LoggerCreatorPtr(new FileLoggerCreator));
    register_logger_creator("OStreamLogger", 
                            LoggerCreatorPtr(new OStreamLoggerCreator));
    register_formatter_creator("DatetimeFormatter", 
                            FormatterCreatorPtr(new DatetimeFormatterCreator));
    register_formatter_creator("StringFormatter", 
                            FormatterCreatorPtr(new StringFormatterCreator));
}

int LogBuilder::register_logger_creator(
    const std::string& name,
    LoggerCreatorPtr creator_ptr
    )
{
    boost::unique_lock<boost::shared_mutex> unique_lock(m_mutex);

    m_logger_creator[name] = creator_ptr;

    return 0;
}

int LogBuilder::register_formatter_creator(
    const std::string& name,
    FormatterCreatorPtr creator_ptr
    )
{
    boost::unique_lock<boost::shared_mutex> unique_lock(m_mutex);

    m_formatter_creator[name] = creator_ptr;

    return 0;   
}

int LogBuilder::build_from_file(
    const std::string& filepath,
    const std::string& type)
{
    try
    {
        boost::property_tree::ptree pt;
        if (type == "json")
        {
            read_json(filepath, pt);
        }
        else if (type == "xml")
        {
            read_xml(filepath, pt);
        }
        else
        {
            CLOGS(kError) << "LogBuilder::build_from_file unknown type:"
                          << type;
        }

        if (pt.find("dispatcher") == pt.not_found())
        {
            CLOGS(kError) << "LogBuilder::build_from_file "
                             "'dispatcher' not exist";
            return -1;          
        }

        if (0 != load_dispatcher(pt.get_child("dispatcher")))
        {
            CLOGS(kError) << "LogBuilder::build_from_file "
                             "load_dispatcher fail";
            return -1;
        }

        return 0;
    }   
    catch (boost::property_tree::json_parser::json_parser_error& ex)
    {
        CLOGS(kError) << "LogBuilder::build_from_file json_parser_error:"
                      << ex.what();
        return -1;
    }
    catch (boost::property_tree::xml_parser::xml_parser_error& ex)
    {
        CLOGS(kError) << "LogBuilder::build_from_file xml_parser_error:"
                      << ex.what();
        return -1;
    }
    catch (boost::property_tree::ptree_bad_path& ex)
    {
       CLOGS(kError) << "LogBuilder::build_from_file "
                     << "tree_bad_path:" << ex.what()
                     << ", path:" << ex.path<std::string>();
        return -1;
    }
    catch (boost::property_tree::ptree_bad_data& ex)
    {
       CLOGS(kError) << "LogBuilder::build_from_file "
                     << "tree_data_path:" << ex.what()
                     << ", data:" << ex.data<std::string>();
        return -1;
    }
}

int LogBuilder::load_dispatcher(boost::property_tree::ptree& setting)
{
    for (boost::property_tree::ptree::iterator it = setting.begin(); 
         it != setting.end(); 
         ++it)
    {
        if (0 != load_runner(it->first, it->second))
        {
            return -1;
        }
    }

    return 0;
}

int LogBuilder::load_runner(
    const std::string& name, 
    boost::property_tree::ptree& setting)
{
    LogOption option;

    if (0 != load_option(setting, option))
    {
        CLOGS(kError) << "LogBuilder::load_runner load_option fail in logger "
                      << "name:" << name;
        return -1;
    }

    // load logger
    if (setting.find("logger") == setting.not_found())
    {
        CLOGS(kError) << "LogBuilder::load_runner name:"<< name 
                      << ", 'logger' not exist";
        return -1;      
    }
    
    option.logger_ptr = create_logger(setting.get_child("logger"));
    if (!option.logger_ptr)
    {
        CLOGS(kError) << "LogBuilder::load_runner name:" << name
                      << ", create_logger fail";
        return -1;
    }

    // load formatter
    if (setting.find("formatter") == setting.not_found())
    {
        CLOGS(kError) << "LogBuilder::load_runner name:" << name 
                      << ", 'formatter' not exist";
    }
    else
    {
        option.formatter_ptr = create_linked_formatter(setting.get_child("formatter"));

        if (!option.formatter_ptr)
        {
            CLOGS(kError) << "LogBuilder::load_runner name:" << name 
                          << ", create formatter fail";
            return -1;
        }
    }

    if (0 != get_log_dispatcher().add(name, option))
    {
        CLOGS(kError) << "LogBuilder::load_runner add_logger fail name:" 
                      << name;
        return -1;
    }

    CLOGS(kDebug) << "LogBuilder::load_runner add_logger success name:" 
                  << name;

    return 0;
}

int LogBuilder::load_option(
    boost::property_tree::ptree& setting,
    LogOption& option
    )
{
    // load runner property
    // priority string
    std::string str_priority = setting.get<std::string>("priority", "");
    option.priority = LogPriorityHelper::parse_priority(str_priority);
    option.queue_timeout = setting.get<int>("queue_timeout", option.queue_timeout);
    option.queue_buffer = setting.get<int>("queue_buffer", option.queue_buffer);
    option.auto_flush = setting.get<bool>("auto_flush", option.auto_flush);
    option.start_on_create = setting.get<bool>("start_on_create", option.start_on_create);
    option.backend_format = setting.get<bool>("backend_format", option.backend_format);
    str_priority = setting.get<std::string>("force_notify_priority", "");
    option.force_notify_priority = LogPriorityHelper::parse_priority(str_priority);   

    return 0;   
}


LoggerPtr LogBuilder::create_logger(boost::property_tree::ptree& setting)
{
    LoggerCreatorPtr creator_ptr;

    // get type from setting
    std::string type = setting.get<std::string>("type");

    // shared_lock block
    {
        boost::shared_lock<boost::shared_mutex> shared_lock(m_mutex);
        std::map<std::string, LoggerCreatorPtr>::iterator it =
            m_logger_creator.find(type);
        if (it != m_logger_creator.end())
        {
            creator_ptr = it->second;
        }
    }

    if (!creator_ptr)
    {
        CLOGS(kError) << "LogBuilder::create_logger type:" << type 
                      << ", creator not found";
        return LoggerPtr();
    }

    LoggerPtr logger_ptr = creator_ptr->create_logger(setting);
    if (!logger_ptr)
    {
        CLOGS(kError) << "LogBuilder::create_logger type:" << type 
                      << ", create logger fail";
        return LoggerPtr();
    }   

    return logger_ptr;
}

FormatterPtr LogBuilder::create_formatter(boost::property_tree::ptree& setting)
{
    FormatterCreatorPtr creator_ptr;

    // get type from setting
    std::string type = setting.get<std::string>("type");

    // shared_lock block
    {
        boost::shared_lock<boost::shared_mutex> shared_lock(m_mutex);
        std::map<std::string, FormatterCreatorPtr>::iterator it =
            m_formatter_creator.find(type);
        if (it != m_formatter_creator.end())
        {
            creator_ptr = it->second;
        }
    }

    if (!creator_ptr)
    {
        CLOGS(kError) << "LogBuilder::create_formatter type:" << type
                      << ", creator not found";
        return FormatterPtr();
    }

    FormatterPtr formatter_ptr = creator_ptr->create_formatter(setting);
    if (!formatter_ptr)
    {
        CLOGS(kError) << "LogBuilder::create_formatter type:" << type
                      << ", create fail";
        return FormatterPtr();
    }

    return formatter_ptr;
}

FormatterPtr LogBuilder::create_linked_formatter(boost::property_tree::ptree& setting)
{
    FormatterPtr head_ptr;
    FormatterPtr tail_ptr;

    for (boost::property_tree::ptree::iterator it = setting.begin(); 
         it != setting.end(); 
         ++it)
    {
        FormatterPtr current_ptr = create_formatter(it->second);

        if (!current_ptr)
        {
            CLOGS(kError)  << "LogBuilder::create_linked_formatter "
                           << "create_formatter:" << it->first << ", fail";
            return FormatterPtr();
        }

        // if there is a tail formatter
        // link current to tail formatter
        // then set current to be the tail
        if (tail_ptr)
        {
            tail_ptr->set_next(current_ptr);
        }

        tail_ptr = current_ptr;

        // if the head_ptr is not set
        // set current to be the head
        if (!head_ptr)
        {
            head_ptr = current_ptr;
        }
    }

    return head_ptr;
}



}//namespace logger
